package com.daocheng.weblog.jwt.utils;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.security.SignatureException;
import java.sql.Date;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;

/**
 * @ClassName JwtTokenHelper
 * @author: 稻城
 * @date: 2025/3/4
 * @Version 1.0
 * @description: TODO
 */
@Component
public class JwtTokenHelper implements InitializingBean {
    //签发人
    @Value("${jwt.issuer}")
    private String issuer;
    //密钥
    private Key key;
    //JWT解析
    private JwtParser jwtParser;
    /**
     * Token 失效时间（分钟）
     */
    @Value("${jwt.tokenExpireTime}")
    private Long tokenExpireTime;
    /**
     * @return void
     * @author 稻城
     * @Date 2025/3/4 11:01
     * @Param [base64Key]
     * @description 解码配置文件中配置的Base64编码 key 为密钥
     */
    @Value("${jwt.secret}")
    public void setBase64Key(String base64Key) {
        key = Keys.hmacShaKeyFor(Base64.getDecoder().decode(base64Key));
    }

    /**
     * @return void
     * @throws Exception
     * @author 稻城
     * @Date 2025/3/4 11:02
     * @Param []
     * @description 初始化JwtParser
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        // 考虑到不同服务器之间可能存在时钟偏移，setAllowedClockSkewSeconds 用于设置能够容忍的最大的时钟误差
        jwtParser = Jwts.parserBuilder().requireIssuer(issuer).setSigningKey(key).setAllowedClockSkewSeconds(10).build();
    }

    /**
     * @return java.lang.String
     * @author 稻城
     * @Date 2025/3/4 11:07
     * @Param [subject]
     * @description
     */
    public String generateToken(String subject) {
        LocalDateTime now = LocalDateTime.now();
        //Token一个小时后失效
        LocalDateTime expireTime = now.plusHours(tokenExpireTime);
        return Jwts.builder().setSubject(subject)
                .setIssuer(issuer)
                .setIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant()))
                .setExpiration(Date.from(expireTime.atZone(ZoneId.systemDefault()).toInstant()))
                .signWith(key)
                .compact();
    }

    /**
     * @param token
     * @return Jws<Claims>
     * @author 稻城
     * @Date 2025/3/4 11:16
     * @description
     */
    public Jws<Claims> parseToken(String token) {
        try {
            return jwtParser.parseClaimsJws(token);
        } catch (MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {
            throw new BadCredentialsException("Token 不可用", e);
        } catch (ExpiredJwtException e) {
            throw new CredentialsExpiredException("Token 已过期", e);
        }
    }

    /**
     * @param
     * @return String
     * @author 稻城
     * @Date 2025/3/4 11:23
     * @description 生成一个Base64的安全密钥
     */
    private static String generateBase64Key() {
        //生成安全密钥
        Key key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
        //将密钥进行Base64编码
        String base64Key = Base64.getEncoder().encodeToString(key.getEncoded());
        return base64Key;
    }

    /**
     * @param token
     * @return void
     * @author 稻城
     * @Date 2025/3/5 11:38
     * @description 校验token
     */
    public void validateToken(String token) {
        jwtParser.parseClaimsJws(token);
    }

    /**
     * @param token
     * @return String
     * @author 稻城
     * @Date 2025/3/5 11:38
     * @description 从Token中解析出用户名
     */
    public String getUsernameByToken(String token) {
        try {
            Claims claims = jwtParser.parseClaimsJws(token).getBody();
            String username = claims.getSubject();
            return username;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        String key = generateBase64Key();
        System.err.println(key);
    }
}
